package pl.edu.agh.transstat.displaying;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import pl.edu.agh.transstat.simulation.Simulation;
import pl.edu.agh.transstat.statictisc.Function;
import pl.edu.agh.transstat.statictisc.Max;
import pl.edu.agh.transstat.statictisc.Mean;
import pl.edu.agh.transstat.statictisc.Min;
import pl.edu.agh.transstat.statictisc.OneValue;
import pl.edu.agh.transstat.statictisc.StandardDeviation;

public class Chart {
	private String plotFileName = "plot.plt";
	private File plotFile;
	private String WIN_PROGRAMFILES = System.getenv("programfiles");
	private String FILE_SEPARATOR = System.getProperty("file.separator");

	public void plot(List<Simulation> simulations, List<Function> functions,
			List<String> params, boolean oneChart) {
		initPlotFile();
		List<File> files = setPlotData(simulations, functions, params, oneChart);
		runGnuplot();
		// usuwanie plikow
		clearFiles(files);
	}

	private void initPlotFile() {
		plotFile = new File(plotFileName);
		try {

			FileWriter fstream = new FileWriter(plotFile, true);
			BufferedWriter fbw = new BufferedWriter(fstream);
			fbw.write("set term png ");
			fbw.newLine();
			fbw.newLine();
			fbw.close();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
		}
	}

	private List<File> setPlotData(List<Simulation> simulations,
			List<Function> functions, List<String> params, boolean oneChart) {
		HashMap<String, File> files = new HashMap<String, File>();

		List<Function> functionsUsed;
		List<Function> oneValueFunction = new ArrayList<Function>();
		oneValueFunction.add(new OneValue());
		
		int i = 0;
		for (Simulation s : simulations) {
			i++;
			for (String param : params) {
				List<Double> values = s.getResults().get(param);
				if (values!=null){
					// if there is only one value, statistics are senseless - do
					// only one chart with this value
					if (values.size() == 1) {
						functionsUsed = oneValueFunction;
					}
					// else use all given functions
					else
						functionsUsed = functions;

					Collections.sort(values);
					if (oneChart) {
						if (!files.keySet().contains(param + " " + "Mix")) {
							(new File(param + " " + "Mix" + ".dat")).delete();
							files.put(param + " " + "Mix", new File(param + " " + "Mix" + ".dat"));
							addParamToPlotFileOneChart(param + " " + "Mix", simulations.size()+1);
						}
						addValueOneChart(i, s.getName(), functionsUsed, values, param + " " + "Mix.dat");
					} else {
						for (Function f : functionsUsed) {
							String name = param + " " + f.getName();
							// tworzenie pliku z danymi dla danego parametru
							if (!files.keySet().contains(name)) {
								(new File(name + ".dat")).delete();
								files.put(name, new File(name + ".dat"));
								addParamToPlotFile(name);
							}

							double val = f.getValue(values);
							// dopisanie wartosci do pliku dla danego parametru

							addValueToFile(s.getName(), val, name + ".dat");

						}
					}
				}
			}
		}

		return new ArrayList<File>(files.values());
	}

	private void addParamToPlotFile(String param) {
		try {
			FileWriter fstream = new FileWriter(plotFile, true);
			BufferedWriter fbw = new BufferedWriter(fstream);
			fbw.write("set output \"" + param + ".png\"");
			fbw.newLine();
			fbw.write("set boxwidth 0.5\nset style fill solid");
			fbw.newLine();

			fbw.write("#" + param);
			fbw.newLine();
			fbw.write("plot \"" + param
					+ ".dat\" using 2:xticlabels(1) with boxes");
			fbw.newLine();
			fbw.newLine();
			fbw.close();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
		}

	}

	
	private void addParamToPlotFileOneChart(String param, int size) {
		try {
			FileWriter fstream = new FileWriter(plotFile, true);
			BufferedWriter fbw = new BufferedWriter(fstream);
			fbw.write("set output \"" + param + ".png\"");
			fbw.newLine();
			fbw.write("set xrange [0.00000 : " + size + " ] noreverse nowriteback\nset yrange [ 0.00000 : ] noreverse nowriteback");
			fbw.newLine();	
			fbw.write("set boxwidth 0.2 absolute");
			fbw.newLine();

			fbw.write("#" + param);
			fbw.newLine();
			fbw.write("plot \"" + param
					+ ".dat\" using 1:4:3:7:6:xticlabels(2) with candlesticks lt 3 lw 2 whiskerbars");
			fbw.newLine();
			fbw.newLine();
			fbw.close();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
		}

	}
	
	private void addValueToFile(String fileName, double value, String name) {
		try {
			FileWriter fstream = new FileWriter(name, true);
			BufferedWriter fbw = new BufferedWriter(fstream);
			fbw.write(fileName + " " + value + "");
			fbw.newLine();
			fbw.close();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
		}

	}

	private void addValueOneChart(int i, String fileName, List<Function> functions, List<Double> values, String name) {
		try {
			FileWriter fstream = new FileWriter(name, true);
			BufferedWriter fbw = new BufferedWriter(fstream);
			double min = 0.0;
			double max = 0.0;
			double mean = 0.0;
			double dev = 0.0;
			for (Function function : functions) {
				if(function instanceof Min){
					min = function.getValue(values);
				} else if(function instanceof Max){
					max = function.getValue(values);
				} else if(function instanceof Mean){
					mean = function.getValue(values);
				} else if(function instanceof StandardDeviation){
					dev = function.getValue(values);
				}
			}
			fbw.write(i + " " + fileName + " " + min + " " + (mean-dev) + " " + mean + " " + (mean+dev) + " " + max);
			fbw.newLine();
			fbw.close();
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
		}

	}

	private void clearFiles(List<File> files) {
		// delete files with gnuplot dates
		// for (File file : files) {
		// file.delete();
		// }
		plotFile.delete();
	}

	private Process runGnuplot() {
		Process p;
		try {

			String[] commands = {
					"cmd.exe",
					"/c",
					WIN_PROGRAMFILES + FILE_SEPARATOR + "gnuplot\\bin"
							+ FILE_SEPARATOR + "wgnuplot.exe", plotFileName };

			p = Runtime.getRuntime().exec(commands);
			p.waitFor();
			p.getInputStream().close();
			p.getOutputStream().close();
			p.getErrorStream().close();
		} catch (IOException | InterruptedException e) {
			System.out.println("Error: " + e.getMessage());
			e.printStackTrace();
			return null;
		}

		return p;
	}
}
